﻿using Alternet.Common;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using OrBitDataModule.Adaptor;
using OrBitDataModule.Base;
using OrBitDataModule.Common;
using OrBitDataModule.Contract;
using OrBitDataModule.CustomEventHandler;
using OrBitDataModule.FileHelper;
using OrBitDataModule.Interface;
using OrBitDataModule.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OrBitDataModule.Protocol
{
    [ProtocolType("监控文件夹")]
    public class FileSystemMonitor : DataTaskBase
    {
        public FileSystemMonitor(string uniqueId, string dataProtocolScriptId, string configJson, string taskName, string taskProtocolType, string apiName, string username, string password)
        {

            this.UniqueId = uniqueId;
            this.TaskName = taskName;
            this.TaskProtocolType = taskProtocolType;
            this.UserName = username;
            this.Password = password;
            this.ApiName = apiName;
            this.ConfigJson = configJson;
            this.DataProtocolScriptId = dataProtocolScriptId;

        }
        ~FileSystemMonitor()
        {
            this.OnDispose();
        }

        public override IContext Context { get; set; }
        public override string ConfigJson { get; }
        public override string UniqueId { get; }
        public override string DataProtocolScriptId { get; }
        public override string TaskName { get; }
        public override string UserName { get; }
        public override string Password { get; }
        public override string ApiName { get; }
        public override string Token { get; set; }
        public override int Expiration { get; set; } = 0;
        public override bool IsScripDebug { get; set; } = false;

        public override ScriptBase Script { set; get; }
        public override Encoding DefaultEncoding => StringUtils.GetEncoding(watchFile.Encoding);

        public override string ScriptName { set; get; }
        public override string ScriptCode { set; get; }
        public override ScriptRunner ScripterRunner { set; get; }

        public override string TaskProtocolType { get; }
        public override SerialPort SerialPortInstance { get; set; }
        public override SocketTcpClient TcpClientInstance { get; set; }

        private DelayFileSystemWatcher fileSystemWatcher = null;

        private WatchFile watchFile = null;

        public override event EventHandler<TaskEventArgs> OnStateChange;

        public override void OnLoad()
        {
            try
            {
                this.ScripterRunner = new ScriptRunner();
                this.watchFile = Newtonsoft.Json.JsonConvert.DeserializeObject<WatchFile>(ConfigJson);
                this.SettingProperty = new SettingDictionary(watchFile);
            }
            catch (Exception ex)
            {
                Script?.OnError(ex);
                WriteLog(ex.Message);
            }
        }

        public override async void Start()
        {
            try
            {
                OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Starting));
                if (!Directory.Exists(watchFile.WatchPath))
                {
                    WriteLog($"监控目录不存在，任务已停止!");
                    OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Stoped));
                    return;
                }
                if (fileSystemWatcher == null)
                {
                    fileSystemWatcher = fileSystemWatcher ?? new DelayFileSystemWatcher(watchFile.WatchPath, watchFile.FileExtensions, watchFile.ReadFileInterval, watchFile.IsWatchSubDir);
                    fileSystemWatcher.OnFileSystem_Changed += FileSystemWatcher_OnFileSystem_Changed;
                }

                //获取最新的脚本代码
                if (!string.IsNullOrWhiteSpace(this.DataProtocolScriptId))
                {
                    var result = await ScriptApi.GetCodeAsync(this.DataProtocolScriptId);
                    this.ScriptName = result.ScriptName;
                    this.ScriptCode = result.ScriptCode;
                    CompileScript();
                    if (!this.CanRunScript)
                    {
                        OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Stoped));
                        return;
                    }
                }
                Script?.OnLoad();
                fileSystemWatcher?.Start();
                WriteLog($"已启动!");
                Script?.OnStarted();
                if (watchFile.ReadfolderOnStartup)
                {
                    await ReadFileOnStartup();
                }
                OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Started));
            }
            catch (Exception ex)
            {
                Script?.OnError(ex);
                WriteLog(ex.Message);
                OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Stoped));
            }
        }

        public override void Stop()
        {
            try
            {
                OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Stopping));
                fileSystemWatcher?.Stop();
                Context?.Scheduler?.Pause();
                WriteLog($"任务已停止!");
                Script?.OnStoped();
                OnStateChange?.Invoke(this, new TaskEventArgs(this.UniqueId, TaskRunningStatus.Stoped));
            }
            catch (Exception ex)
            {
                Script?.OnError(ex);
                WriteLog(ex.Message);
            }
        }

        private async void FileSystemWatcher_OnFileSystem_Changed(object sender, ProcessedFileSystemEventArgs e)
        {
            if ((e.ChangeType == WatcherChangeTypes.Created && watchFile.IsFileCreated) || (e.ChangeType == WatcherChangeTypes.Changed && watchFile.IsFileChanged))
            {
                try
                {
                    await PorcessFile(sender, e);
                }
                catch (Exception ex)
                {
                    Script?.OnError(ex);
                    WriteLog(ex.Message);
                }
            }
        }

        /// <summary>
        /// 启动时读取已存在的文件
        /// </summary>
        /// <returns></returns>
        private async Task ReadFileOnStartup()
        {
            List<string> files = FileUtils.TraverseFolder(watchFile.WatchPath, watchFile.FileExtensions, watchFile.IsWatchSubDir);
            foreach (string file in files)
            {
                ProcessedFileSystemEventArgs eventArgs = new ProcessedFileSystemEventArgs(WatcherChangeTypes.Created, file, Path.GetFileName(file));
                await PorcessFile(fileSystemWatcher, eventArgs);
            }
        }

        /// <summary>
        /// 处理文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private async Task PorcessFile(object sender, ProcessedFileSystemEventArgs e)
        {
            if (File.Exists(e.FullPath))
            {
                try
                {
                    WriteLog($"文件：{e.FullPath}已{Enum.GetName(typeof(WatcherChangeTypes), e.ChangeType)}");
                    // 当脚本中已处理，就以脚本事件参数中e.Content内容为准，未处理就按默认文本读取处理
                    string text = string.Empty;
                    if (string.IsNullOrWhiteSpace(watchFile.FileType) || watchFile.FileType == "文本")
                    {
                        text = await FileUtils.ReadTextFileAsync(e.FullPath, StringUtils.GetEncoding(watchFile.Encoding));
                    }
                    else if (watchFile.FileType == "Excel")
                    {
                        text = FileUtils.ReadExcelString(e.FullPath);
                    }
                    else if (watchFile.FileType == "CSV")
                    {
                        text = FileUtils.ReadCSVString(e.FullPath, StringUtils.GetEncoding(watchFile.Encoding));
                    }

                    e.Content = text;
                    ProcessedFileSystemEventArgs eventArgs = e;
                    Script?.OnFileSystem_Changed(sender, eventArgs);
                    string content = Script == null ? text : eventArgs.Content;
                    if (eventArgs.IsSubmit)
                    {
                        if (string.IsNullOrWhiteSpace(Token))
                        {
                            GetWebApiToken();
                            if (string.IsNullOrWhiteSpace(Token))
                            {
                                WriteLog("接口未能获取到token，请检查", LogLevel.Error);
                                return;
                            }
                        }

                        InputParameter inputParameter = new InputParameter()
                        {
                            TaskName = TaskName,
                            ApiName = ApiName,
                            UserName = UserName,
                            ProtocolType = "监控文件夹",
                            ClientIP = NetworkUtils.GetLocalIPAddresses(),
                            ClientMAC = NetworkUtils.GetLocalMACAddress(),
                            ClientHostName = NetworkUtils.GetComputerName(),
                            Parameter1= JsonConvert.SerializeObject(LocalSetting.GetConfig(this.UniqueId).parameters),
                            Parameter2 = e.FullPath
                        };
                        Script?.BeforeSubmit(inputParameter, content);
                        var result = await WebApi.IntegrationApiPostAsync(ApiName, Token, inputParameter, content);
                        WriteLog($"文件：{e.FullPath}，已提交至接口：{ApiName},返回:{result.Result},信息:{result.ReturnMessage}", LogLevel.Info);
                        Script?.AfterSubmit(result.Result, result.ReturnMessage, result.OutputResponse);
                    }
                    if (!string.IsNullOrWhiteSpace(watchFile.BackupFilePath))
                    {
                        if (eventArgs.IsMoveToBackupDirectory)
                        {
                            string dstFileName = FileUtils.MoveFile(e.FullPath, watchFile.BackupFilePath);
                            WriteLog($"文件：{e.FullPath}已移动到备份目录:{dstFileName}", LogLevel.Info);
                        }
                    }
                }
                catch (Exception ex)
                {
                    WriteLog($"Api接口：{ApiName},提交数据出现异常！信息:{ex.Message}");
                }
            }
        }

        public override string SendExternal(string message) => string.Empty;

        public override void OnDispose()
        {
            fileSystemWatcher?.Stop();
            watchFile = null;
            fileSystemWatcher = null;
            Script?.OnDispose();
        }
    }
}
